{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<a name=\"top\"></a><img src=\"./source/SpinalHDL.png\" alt=\"SpinalHDL based on Scala\" style=\"width:320px;\" />"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "  Before running Spinal HDL code, be sure to load SpinalHDL Libraries  \n",
    "**Note** : This may be a little slow when the first time load, please wait a moment to download Lib from remote.)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "val path = System.getProperty(\"user.dir\") + \"/source/load-spinal.sc\"\n",
    "interp.load.module(ammonite.ops.Path(java.nio.file.FileSystems.getDefault().getPath(path)))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## [Fractal calculator](https://spinalhdl.github.io/SpinalDoc-RTD/SpinalHDL/Examples/Intermediates%20ones/fractal.html)\n",
    "#### Elaboration parameters (Generics)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "case class PixelSolverGenerics(fixAmplitude : Int,\n",
    "                               fixResolution : Int,\n",
    "                               iterationLimit : Int){\n",
    "  val iterationWidth = log2Up(iterationLimit+1)\n",
    "  def iterationType = UInt(iterationWidth bits)\n",
    "  def fixType = SFix(\n",
    "    peak = fixAmplitude exp,\n",
    "    resolution = fixResolution exp\n",
    "  )\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Bundle definition"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "case class PixelTask(g : PixelSolverGenerics) extends Bundle{\n",
    "  val x,y = g.fixType\n",
    "}\n",
    "\n",
    "case class PixelResult(g : PixelSolverGenerics) extends Bundle{\n",
    "  val iteration = g.iterationType\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Component implementation"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "case class PixelSolver(g : PixelSolverGenerics) extends Component{\n",
    "  val io = new Bundle{\n",
    "    val cmd = slave  Stream(PixelTask(g))\n",
    "    val rsp = master Stream(PixelResult(g))\n",
    "  }\n",
    "\n",
    "  import g._\n",
    "\n",
    "  //Define states\n",
    "  val x,y       = Reg(fixType) init(0)\n",
    "  val iteration = Reg(iterationType) init(0)\n",
    "\n",
    "  //Do some shared calculation\n",
    "  val xx = x*x\n",
    "  val yy = y*y\n",
    "  val xy = x*y\n",
    "\n",
    "  //Apply default assignment\n",
    "  io.cmd.ready := False\n",
    "  io.rsp.valid := False\n",
    "  io.rsp.iteration := iteration\n",
    "\n",
    "  when(io.cmd.valid) {\n",
    "    //Is the mandelbrot iteration done ?\n",
    "    when(xx + yy >= 4.0 || iteration === iterationLimit) {\n",
    "      io.rsp.valid := True\n",
    "      when(io.rsp.ready){\n",
    "        io.cmd.ready := True\n",
    "        x := 0\n",
    "        y := 0\n",
    "        iteration := 0\n",
    "      }\n",
    "    } otherwise {\n",
    "      x := (xx - yy + io.cmd.x).truncated\n",
    "      y := (((xy) << 1) + io.cmd.y).truncated\n",
    "      iteration := iteration + 1\n",
    "    }\n",
    "  }\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## [UART](https://spinalhdl.github.io/SpinalDoc-RTD/SpinalHDL/Examples/Intermediates%20ones/uart.html)\n",
    "#### Controller construction parameters"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "case class UartCtrlGenerics( dataWidthMax: Int = 8,\n",
    "                             clockDividerWidth: Int = 20, // baudrate = Fclk / rxSamplePerBit / clockDividerWidth\n",
    "                             preSamplingSize: Int = 1,\n",
    "                             samplingSize: Int = 5,\n",
    "                             postSamplingSize: Int = 2) {\n",
    "  val rxSamplePerBit = preSamplingSize + samplingSize + postSamplingSize\n",
    "  assert(isPow2(rxSamplePerBit))\n",
    "  if ((samplingSize % 2) == 0)\n",
    "    SpinalWarning(s\"It's not nice to have a odd samplingSize value (because of the majority vote)\")\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### UART bus"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "case class Uart() extends Bundle with IMasterSlave {\n",
    "  val txd = Bool()\n",
    "  val rxd = Bool()\n",
    "\n",
    "  override def asMaster(): Unit = {\n",
    "    out(txd)\n",
    "    in(rxd)\n",
    "  }\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### UART configuration enums"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "object UartParityType extends SpinalEnum(sequancial) {\n",
    "  val NONE, EVEN, ODD = newElement()\n",
    "}\n",
    "\n",
    "object UartStopType extends SpinalEnum(sequancial) {\n",
    "  val ONE, TWO = newElement()\n",
    "  def toBitCount(that : T) : UInt = (that === ONE) ? U\"0\" | U\"1\"\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### UartCtrl configuration Bundles"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "case class UartCtrlFrameConfig(g: UartCtrlGenerics) extends Bundle {\n",
    "  val dataLength = UInt(log2Up(g.dataWidthMax) bit) //Bit count = dataLength + 1\n",
    "  val stop       = UartStopType()\n",
    "  val parity     = UartParityType()\n",
    "}\n",
    "\n",
    "case class UartCtrlConfig(g: UartCtrlGenerics) extends Bundle {\n",
    "  val frame        = UartCtrlFrameConfig(g)\n",
    "  val clockDivider = UInt (g.clockDividerWidth bit) //see UartCtrlGenerics.clockDividerWidth for calculation\n",
    "\n",
    "  def setClockDivider(baudrate : Double,clkFrequency : Double = ClockDomain.current.frequency.getValue) : Unit = {\n",
    "    clockDivider := (clkFrequency / baudrate / g.rxSamplePerBit).toInt\n",
    "  }\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### UartCtrlTx"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "object UartCtrlTxState extends SpinalEnum {\n",
    "  val IDLE, START, DATA, PARITY, STOP = newElement()\n",
    "}\n",
    "\n",
    "class UartCtrlTx(g : UartCtrlGenerics) extends Component {\n",
    "  import g._\n",
    "\n",
    "  val io = new Bundle {\n",
    "    val configFrame  = in(UartCtrlFrameConfig(g))\n",
    "    val samplingTick = in Bool()\n",
    "    val write        = slave Stream (Bits(dataWidthMax bit))\n",
    "    val txd          = out Bool()\n",
    "  }\n",
    "\n",
    "  // Provide one clockDivider.tick each rxSamplePerBit pulse of io.samplingTick\n",
    "  // Used by the stateMachine as a baud rate time reference\n",
    "  val clockDivider = new Area {\n",
    "    val counter = Reg(UInt(log2Up(rxSamplePerBit) bits)) init(0)\n",
    "    val tick = False\n",
    "    when(io.samplingTick) {\n",
    "      counter := counter - 1\n",
    "      tick := counter === 0\n",
    "    }\n",
    "  }\n",
    "\n",
    "  // Count up each clockDivider.tick, used by the state machine to count up data bits and stop bits\n",
    "  val tickCounter = new Area {\n",
    "    val value = Reg(UInt(Math.max(dataWidthMax, 2) bit))\n",
    "    def reset() = value := 0\n",
    "\n",
    "    when(clockDivider.tick) {\n",
    "      value := value + 1\n",
    "    }\n",
    "  }\n",
    "\n",
    "  val stateMachine = new Area {\n",
    "    import UartCtrlTxState._\n",
    "\n",
    "    val state = RegInit(IDLE)\n",
    "    val parity = Reg(Bool())\n",
    "    val txd = True\n",
    "\n",
    "    when(clockDivider.tick) {\n",
    "      parity := parity ^ txd\n",
    "    }\n",
    "\n",
    "    io.write.ready := False\n",
    "    switch(state) {\n",
    "      is(IDLE){\n",
    "        when(io.write.valid && clockDivider.tick){\n",
    "          state := START\n",
    "        }\n",
    "      }\n",
    "      is(START) {\n",
    "        txd := False\n",
    "        when(clockDivider.tick) {\n",
    "          state := DATA\n",
    "          parity := io.configFrame.parity === UartParityType.ODD\n",
    "          tickCounter.reset()\n",
    "        }\n",
    "      }\n",
    "      is(DATA) {\n",
    "        txd := io.write.payload(tickCounter.value)\n",
    "        when(clockDivider.tick) {\n",
    "          when(tickCounter.value === io.configFrame.dataLength) {\n",
    "            io.write.ready := True\n",
    "            tickCounter.reset()\n",
    "            when(io.configFrame.parity === UartParityType.NONE) {\n",
    "              state := STOP\n",
    "            } otherwise {\n",
    "              state := PARITY\n",
    "            }\n",
    "          }\n",
    "        }\n",
    "      }\n",
    "      is(PARITY) {\n",
    "        txd := parity\n",
    "        when(clockDivider.tick) {\n",
    "          state := STOP\n",
    "          tickCounter.reset()\n",
    "        }\n",
    "      }\n",
    "      is(STOP) {\n",
    "        when(clockDivider.tick) {\n",
    "          when(tickCounter.value === toBitCount(io.configFrame.stop)) {\n",
    "            state := io.write.valid ? START | IDLE\n",
    "          }\n",
    "        }\n",
    "      }\n",
    "    }\n",
    "  }\n",
    "\n",
    "  io.txd := RegNext(stateMachine.txd, True)\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### UartCtrlRx"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "object UartCtrlRxState extends SpinalEnum {\n",
    "  val IDLE, START, DATA, PARITY, STOP = newElement()\n",
    "}\n",
    "\n",
    "class UartCtrlRx(g : UartCtrlGenerics) extends Component {\n",
    "  import g._\n",
    "  val io = new Bundle {\n",
    "    val configFrame  = in(UartCtrlFrameConfig(g))\n",
    "    val samplingTick = in Bool()\n",
    "    val read         = master Flow (Bits(dataWidthMax bit))\n",
    "    val rxd          = in Bool()\n",
    "  }\n",
    "\n",
    "  // Implement the rxd sampling with a majority vote over samplingSize bits\n",
    "  // Provide a new sampler.value each time sampler.tick is high\n",
    "  val sampler = new Area {\n",
    "    val syncroniser = BufferCC(io.rxd)\n",
    "    val samples     = History(that=syncroniser,when=io.samplingTick,length=samplingSize)\n",
    "    val value       = RegNext(MajorityVote(samples))\n",
    "    val tick        = RegNext(io.samplingTick)\n",
    "  }\n",
    "\n",
    "  // Provide a bitTimer.tick each rxSamplePerBit\n",
    "  // reset() can be called to recenter the counter over a start bit.\n",
    "  val bitTimer = new Area {\n",
    "    val counter = Reg(UInt(log2Up(rxSamplePerBit) bit))\n",
    "    def reset() = counter := preSamplingSize + (samplingSize - 1) / 2 - 1\n",
    "    val tick = False\n",
    "    when(sampler.tick) {\n",
    "      counter := counter - 1\n",
    "      when(counter === 0) {\n",
    "        tick := True\n",
    "      }\n",
    "    }\n",
    "  }\n",
    "\n",
    "  // Provide bitCounter.value that count up each bitTimer.tick, Used by the state machine to count data bits and stop bits\n",
    "  // reset() can be called to reset it to zero\n",
    "  val bitCounter = new Area {\n",
    "    val value = Reg(UInt(Math.max(dataWidthMax, 2) bit))\n",
    "    def reset() = value := 0\n",
    "\n",
    "    when(bitTimer.tick) {\n",
    "      value := value + 1\n",
    "    }\n",
    "  }\n",
    "\n",
    "  val stateMachine = new Area {\n",
    "    import UartCtrlRxState._\n",
    "\n",
    "    val state   = RegInit(IDLE)\n",
    "    val parity  = Reg(Bool())\n",
    "    val shifter = Reg(io.read.payload)\n",
    "\n",
    "    //Parity calculation\n",
    "    when(bitTimer.tick) {\n",
    "      parity := parity ^ sampler.value\n",
    "    }\n",
    "\n",
    "    io.read.valid := False\n",
    "    switch(state) {\n",
    "      is(IDLE) {\n",
    "        when(sampler.value === False) {\n",
    "          state := START\n",
    "          bitTimer.reset()\n",
    "        }\n",
    "      }\n",
    "      is(START) {\n",
    "        when(bitTimer.tick) {\n",
    "          state := DATA\n",
    "          bitCounter.reset()\n",
    "          parity := io.configFrame.parity === UartParityType.ODD\n",
    "          when(sampler.value === True) {\n",
    "            state := IDLE\n",
    "          }\n",
    "        }\n",
    "      }\n",
    "      is(DATA) {\n",
    "        when(bitTimer.tick) {\n",
    "          shifter(bitCounter.value) := sampler.value\n",
    "          when(bitCounter.value === io.configFrame.dataLength) {\n",
    "            bitCounter.reset()\n",
    "            when(io.configFrame.parity === UartParityType.NONE) {\n",
    "              state := STOP\n",
    "            } otherwise {\n",
    "              state := PARITY\n",
    "            }\n",
    "          }\n",
    "        }\n",
    "      }\n",
    "      is(PARITY) {\n",
    "        when(bitTimer.tick) {\n",
    "          state := STOP\n",
    "          bitCounter.reset()\n",
    "          when(parity =/= sampler.value) {\n",
    "            state := IDLE\n",
    "          }\n",
    "        }\n",
    "      }\n",
    "      is(STOP) {\n",
    "        when(bitTimer.tick) {\n",
    "          when(!sampler.value) {\n",
    "            state := IDLE\n",
    "          }.elsewhen(bitCounter.value === toBitCount(io.configFrame.stop)) {\n",
    "            state := IDLE\n",
    "            io.read.valid := True\n",
    "          }\n",
    "        }\n",
    "      }\n",
    "    }\n",
    "  }\n",
    "  io.read.payload := stateMachine.shifter\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### UartCtrl"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "class UartCtrl(g : UartCtrlGenerics = UartCtrlGenerics()) extends Component {\n",
    "  val io = new Bundle {\n",
    "    val config = in(UartCtrlConfig(g))\n",
    "    val write  = slave(Stream(Bits(g.dataWidthMax bit)))\n",
    "    val read   = master(Flow(Bits(g.dataWidthMax bit)))\n",
    "    val uart   = master(Uart())\n",
    "  }\n",
    "\n",
    "  val tx = new UartCtrlTx(g)\n",
    "  val rx = new UartCtrlRx(g)\n",
    "\n",
    "  //Clock divider used by RX and TX\n",
    "  val clockDivider = new Area {\n",
    "    val counter = Reg(UInt(g.clockDividerWidth bits)) init(0)\n",
    "    val tick = counter === 0\n",
    "\n",
    "    counter := counter - 1\n",
    "    when(tick) {\n",
    "      counter := io.config.clockDivider\n",
    "    }\n",
    "  }\n",
    "\n",
    "  tx.io.samplingTick := clockDivider.tick\n",
    "  rx.io.samplingTick := clockDivider.tick\n",
    "\n",
    "  tx.io.configFrame := io.config.frame\n",
    "  rx.io.configFrame := io.config.frame\n",
    "\n",
    "  tx.io.write << io.write\n",
    "  rx.io.read >> io.read\n",
    "\n",
    "  io.uart.txd <> tx.io.txd\n",
    "  io.uart.rxd <> rx.io.rxd\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Simple usage"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "val uartCtrl: UartCtrl = UartCtrl(\n",
    "  config = UartCtrlInitConfig(\n",
    "    baudrate = 115200,\n",
    "    dataLength = 7,  // 8 bits\n",
    "    parity = UartParityType.NONE,\n",
    "    stop = UartStopType.ONE\n",
    "  )\n",
    ")\n",
    "// If you are using txd pin only:\n",
    "uartCtrl.io.uart.rxd := True  // High is the idle state for UART\n",
    "txd := uartCtrl.io.uart.txd\n",
    "\n",
    "// On the contrary, if you are using rxd pin only:\n",
    "\n",
    "val uartCtrl: UartCtrl = UartCtrl(\n",
    "  config = UartCtrlInitConfig(\n",
    "    baudrate = 115200,\n",
    "    dataLength = 7,  // 8 bits\n",
    "    parity = UartParityType.NONE,\n",
    "    stop = UartStopType.ONE\n",
    "  ),\n",
    "  readonly = true\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Example with test bench"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "class UartCtrlUsageExample extends Component{\n",
    "  val io = new Bundle{\n",
    "    val uart = master(Uart())\n",
    "    val switchs = in Bits(8 bits)\n",
    "    val leds = out Bits(8 bits)\n",
    "  }\n",
    "\n",
    "  val uartCtrl = new UartCtrl()\n",
    "  uartCtrl.io.config.setClockDivider(921600)\n",
    "  uartCtrl.io.config.frame.dataLength := 7  //8 bits\n",
    "  uartCtrl.io.config.frame.parity := UartParityType.NONE\n",
    "  uartCtrl.io.config.frame.stop := UartStopType.ONE\n",
    "  uartCtrl.io.uart <> io.uart\n",
    "\n",
    "  //Assign io.led with a register loaded each time a byte is received\n",
    "  io.leds := uartCtrl.io.read.toReg()\n",
    "\n",
    "  //Write the value of switch on the uart each 2000 cycles\n",
    "  val write = Stream(Bits(8 bits))\n",
    "  write.valid := CounterFreeRun(2000).willOverflow\n",
    "  write.payload := io.switchs\n",
    "  write >-> uartCtrl.io.write\n",
    "\n",
    "  //The following example is just a \"mad one\" but if you want to send a 0x55 header before sending the value of switches,\n",
    "  //you can replace the write generator of the preceding example by:\n",
    "  // val write = Stream(Fragment(Bits(8 bits)))\n",
    "  // write.valid := CounterFreeRun(4000).willOverflow\n",
    "  // write.fragment := io.switchs\n",
    "  // write.last := True\n",
    "  // write.stage().insertHeader(0x55).toStreamOfFragment >> uartCtrl.io.write\n",
    "}\n",
    "\n",
    "object UartCtrlUsageExample{\n",
    "  def main(args: Array[String]) {\n",
    "    SpinalVhdl(new UartCtrlUsageExample,defaultClockDomainFrequency=FixedFrequency(50e6))\n",
    "  }\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Bonus: Having fun with Stream"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "// If you want to queue data received from the UART:\n",
    "val uartCtrl = new UartCtrl()\n",
    "val queuedReads = uartCtrl.io.read.toStream.queue(16)\n",
    "\n",
    "// If you want to add a queue on the write interface and do some flow control:\n",
    "val uartCtrl = new UartCtrl()\n",
    "val writeCmd = Stream(Bits(8 bits))\n",
    "val stopIt = Bool()\n",
    "writeCmd.queue(16).haltWhen(stopIt) >> uartCtrl.io.write"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## [VGA](https://spinalhdl.github.io/SpinalDoc-RTD/SpinalHDL/Examples/Intermediates%20ones/vga.html)\n",
    "#### RGB color"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "case class RgbConfig(rWidth : Int,gWidth : Int,bWidth : Int){\n",
    "  def getWidth = rWidth + gWidth + bWidth\n",
    "}\n",
    "\n",
    "case class Rgb(c: RgbConfig) extends Bundle{\n",
    "  val r = UInt(c.rWidth bit)\n",
    "  val g = UInt(c.gWidth bit)\n",
    "  val b = UInt(c.bWidth bit)\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### VGA bus"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "case class Vga (rgbConfig: RgbConfig) extends Bundle with IMasterSlave{\n",
    "  val vSync = Bool()\n",
    "  val hSync = Bool()\n",
    "\n",
    "  val colorEn = Bool()\n",
    "  val color   = Rgb(rgbConfig)\n",
    "\n",
    "  override def asMaster() : Unit = this.asOutput()\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### VGA timings"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "case class VgaTimingsHV(timingsWidth: Int) extends Bundle {\n",
    "  val colorStart = UInt(timingsWidth bit)\n",
    "  val colorEnd   = UInt(timingsWidth bit)\n",
    "  val syncStart  = UInt(timingsWidth bit)\n",
    "  val syncEnd    = UInt(timingsWidth bit)\n",
    "}\n",
    "\n",
    "case class VgaTimings(timingsWidth: Int) extends Bundle {\n",
    "  val h = VgaTimingsHV(timingsWidth)\n",
    "  val v = VgaTimingsHV(timingsWidth)\n",
    "\n",
    "  def setAs_h640_v480_r60: Unit = {\n",
    "    h.syncStart := 96 - 1\n",
    "    h.syncEnd := 800 - 1\n",
    "    h.colorStart := 96 + 16 - 1\n",
    "    h.colorEnd := 800 - 48 - 1\n",
    "    v.syncStart := 2 - 1\n",
    "    v.syncEnd := 525 - 1\n",
    "    v.colorStart := 2 + 10 - 1\n",
    "    v.colorEnd := 525 - 33 - 1\n",
    "  }\n",
    "\n",
    "  def setAs_h64_v64_r60: Unit = {\n",
    "    h.syncStart := 96 - 1\n",
    "    h.syncEnd := 800 - 1\n",
    "    h.colorStart := 96 + 16 - 1 + 288\n",
    "    h.colorEnd := 800 - 48 - 1 - 288\n",
    "    v.syncStart := 2 - 1\n",
    "    v.syncEnd := 525 - 1\n",
    "    v.colorStart := 2 + 10 - 1 + 208\n",
    "    v.colorEnd := 525 - 33 - 1 - 208\n",
    "  }\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### VGA Controller"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "class VgaCtrl(rgbConfig: RgbConfig, timingsWidth: Int = 12) extends Component {\n",
    "  // Component and io definition\n",
    "  val io = new Bundle {\n",
    "    val softReset = in Bool()\n",
    "    val timings = in(VgaTimings(timingsWidth))\n",
    "    val pixels = slave Stream (Rgb(rgbConfig))\n",
    "\n",
    "    val error = out Bool()\n",
    "    val frameStart = out Bool()\n",
    "    val vga = master(Vga(rgbConfig))\n",
    "  }\n",
    "\n",
    "  // Horizontal and vertical logic\n",
    "  case class HVArea(timingsHV: VgaTimingsHV, enable: Bool) extends Area {\n",
    "    val counter = Reg(UInt(timingsWidth bit)) init(0)\n",
    "\n",
    "    val syncStart  = counter === timingsHV.syncStart\n",
    "    val syncEnd    = counter === timingsHV.syncEnd\n",
    "    val colorStart = counter === timingsHV.colorStart\n",
    "    val colorEnd   = counter === timingsHV.colorEnd\n",
    "\n",
    "    when(enable) {\n",
    "      counter := counter + 1\n",
    "      when(syncEnd) {\n",
    "        counter := 0\n",
    "      }\n",
    "    }\n",
    "\n",
    "    val sync    = RegInit(False) setWhen(syncStart) clearWhen(syncEnd)\n",
    "    val colorEn = RegInit(False) setWhen(colorStart) clearWhen(colorEnd)\n",
    "\n",
    "    when(io.softReset) {\n",
    "      counter := 0\n",
    "      sync    := False\n",
    "      colorEn := False\n",
    "    }\n",
    "  }\n",
    "  val h = HVArea(io.timings.h, True)\n",
    "  val v = HVArea(io.timings.v, h.syncEnd)\n",
    "\n",
    "  // Interconnections\n",
    "  val colorEn = h.colorEn && v.colorEn\n",
    "  io.pixels.ready := colorEn\n",
    "  io.error := colorEn && ! io.pixels.valid\n",
    "\n",
    "  io.frameStart := v.syncEnd\n",
    "\n",
    "  io.vga.hSync := h.sync\n",
    "  io.vga.vSync := v.sync\n",
    "  io.vga.colorEn := colorEn\n",
    "  io.vga.color := io.pixels.payload\n",
    "\n",
    "  // Bonus\n",
    "  def feedWith(that : Stream[Fragment[Rgb]]): Unit ={\n",
    "    io.pixels << that.toStreamOfFragment\n",
    "\n",
    "    val error = RegInit(False)\n",
    "    when(io.error){\n",
    "      error := True\n",
    "    }\n",
    "    when(that.isLast){\n",
    "      error := False\n",
    "    }\n",
    "\n",
    "    io.softReset := error\n",
    "    when(error){\n",
    "      that.ready := True\n",
    "    }\n",
    "  }\n",
    "}"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Scala",
   "language": "scala",
   "name": "scala"
  },
  "language_info": {
   "codemirror_mode": "text/x-scala",
   "file_extension": ".scala",
   "mimetype": "text/x-scala",
   "name": "scala",
   "nbconvert_exporter": "script",
   "version": "2.11.12"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
